<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="robots" content="noodp" />
        <title>右值引用和完美转发 - 欢迎来到SY的博客</title><meta name="description" content="右值引用和完美转发"><meta property="og:title" content="右值引用和完美转发" />
<meta property="og:description" content="右值引用和完美转发" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" /><meta property="og:image" content="https://acking-you.gitee.io/logo.png"/><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2022-08-16T00:00:00+00:00" />
<meta property="article:modified_time" content="2022-08-16T00:00:00+00:00" />

<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://acking-you.gitee.io/logo.png"/>

<meta name="twitter:title" content="右值引用和完美转发"/>
<meta name="twitter:description" content="右值引用和完美转发"/>
<meta name="application-name" content="FeelIt">
<meta name="apple-mobile-web-app-title" content="FeelIt"><meta name="theme-color" content="#ffffff"><meta name="msapplication-TileColor" content="#da532c"><link rel="canonical" href="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" /><link rel="prev" href="https://acking-you.gitee.io/posts/%C3%A9%C3%A5%C2%BA%C3%A6%C3%A5%C3%A7%C3%A5%C3%A6%C3%A7/" /><link rel="next" href="https://acking-you.gitee.io/posts/%C3%A8%C2%BA%C3%A6%C3%A7%C3%A9%C2%B5%C3%A2-/" /><link rel="stylesheet" href="/css/page.min.css"><link rel="stylesheet" href="/css/home.min.css"><script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "BlogPosting",
        "headline": "右值引用和完美转发",
        "inLanguage": "zh-CN",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "https:\/\/acking-you.gitee.io\/posts\/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5\/"
        },"genre": "posts","keywords": "右值引用和完美转发","wordcount":  4645 ,
        "url": "https:\/\/acking-you.gitee.io\/posts\/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5\/","datePublished": "2022-08-16T00:00:00+00:00","dateModified": "2022-08-16T00:00:00+00:00","publisher": {
            "@type": "Organization",
            "name": "作者"},"author": {
                "@type": "Person",
                "name": "作者"
            },"description": "右值引用和完美转发"
    }
    </script></head><body data-header-desktop="auto" data-header-mobile="auto"><script>(window.localStorage && localStorage.getItem('theme') ? localStorage.getItem('theme') === 'dark' : ('auto' === 'auto' ? window.matchMedia('(prefers-color-scheme: dark)').matches : 'auto' === 'dark')) && document.body.setAttribute('theme', 'dark');</script>

        <div id="mask"></div><div class="wrapper"><header class="desktop" id="header-desktop">
    <div class="header-wrapper">
        <div class="header-title">
            <a href="/" title="欢迎来到SY的博客">欢迎来到SY的博客</a>
        </div>
        <div class="menu">
            <div class="menu-inner"><a class="menu-item" href="/posts/"> 文章 </a><a class="menu-item" href="/tags/"> 标签 </a><a class="menu-item" href="/categories/"> 分类 </a><span class="menu-item delimiter"></span><span class="menu-item search" id="search-desktop">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-desktop">
                        <a href="#" class="search-button search-toggle" id="search-toggle-desktop" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="#" class="search-button search-clear" id="search-clear-desktop" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-desktop">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </span><a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div>
        </div>
    </div>
</header><header class="mobile" id="header-mobile">
    <div class="header-container">
        <div class="header-wrapper">
            <div class="header-title">
                <a href="/" title="欢迎来到SY的博客">欢迎来到SY的博客</a>
            </div>
            <div class="menu-toggle" id="menu-toggle-mobile">
                <span></span><span></span><span></span>
            </div>
        </div>
        <div class="menu" id="menu-mobile"><div class="search-wrapper">
                    <div class="search mobile" id="search-mobile">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-mobile">
                        <a href="#" class="search-button search-toggle" id="search-toggle-mobile" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="#" class="search-button search-clear" id="search-clear-mobile" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-mobile">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </div>
                    <a href="#" class="search-cancel" id="search-cancel-mobile">
                        取消
                    </a>
                </div><a class="menu-item" href="/posts/" title="">文章</a><a class="menu-item" href="/tags/" title="">标签</a><a class="menu-item" href="/categories/" title="">分类</a><div class="menu-item"><a href="javascript:void(0);" class="theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div></div>
    </div>
</header><div class="search-dropdown desktop">
    <div id="search-dropdown-desktop"></div>
</div>
<div class="search-dropdown mobile">
    <div id="search-dropdown-mobile"></div>
</div><main class="main">
                <div class="container"><div class="toc" id="toc-auto">
            <h2 class="toc-title">目录</h2>
            <div class="toc-content" id="toc-content-auto"></div>
        </div><article class="page single" data-toc="disable"><div class="featured-image"><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/img_convert/c592952aec4e1c875073f2a3d970cde5.png#pic_center"
        data-srcset="https://img-blog.csdnimg.cn/img_convert/c592952aec4e1c875073f2a3d970cde5.png#pic_center, https://img-blog.csdnimg.cn/img_convert/c592952aec4e1c875073f2a3d970cde5.png#pic_center 1.5x, https://img-blog.csdnimg.cn/img_convert/c592952aec4e1c875073f2a3d970cde5.png#pic_center 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/img_convert/c592952aec4e1c875073f2a3d970cde5.png#pic_center"
        title="右值引用和完美转发" /></div><div class="single-card" data-image="true"><h2 class="single-title animated flipInX">右值引用和完美转发</h2><div class="post-meta">
                <div class="post-meta-line"><span class="post-author"><a href="/" title="Author" rel=" author" class="author"><i class="fas fa-user-circle fa-fw"></i>作者</a></span>&nbsp;<span class="post-category">出版于  <a href="/categories/c++11%E7%9A%84%E5%AD%A6%E4%B9%A0/"><i class="far fa-folder fa-fw"></i>C++11的学习</a></span></div>
                <div class="post-meta-line"><span><i class="far fa-calendar-alt fa-fw"></i>&nbsp;<time datetime="2022-08-16">2022-08-16</time></span>&nbsp;<span><i class="fas fa-pencil-alt fa-fw"></i>&nbsp;约 4645 字</span>&nbsp;
                    <span><i class="far fa-clock fa-fw"></i>&nbsp;预计阅读 10 分钟</span>&nbsp;</div>
            </div>
            
            <hr><div class="details toc" id="toc-static"  data-kept="">
                    <div class="details-summary toc-title">
                        <span>目录</span>
                        <span><i class="details-icon fas fa-angle-right"></i></span>
                    </div>
                    <div class="details-content toc-content" id="toc-content-static"><nav id="TableOfContents">
  <ul>
    <li><a href="#1右值引用的简介">1、右值引用的简介</a></li>
    <li><a href="#2右值引用的使用">2、右值引用的使用</a></li>
    <li><a href="#3性能优化">3、性能优化</a></li>
    <li><a href="#4的特性">4、&amp;&amp;的特性</a></li>
    <li><a href="#5stdmove">5、std::move</a></li>
    <li><a href="#6stdforward">6、std::forward</a></li>
  </ul>
</nav></div>
                </div><div class="content" id="content"><h2 id="1右值引用的简介">1、右值引用的简介</h2>
<p>C++11增加了一个新的类型，称为<strong>右值引用(R-value reference)</strong>，标记为 **&amp;&amp;。**在介绍右值引用类型之前要先了解什么是左值和右值：</p>
<ul>
<li>lvalue是<strong>locator value</strong>的缩写，rvalue是 <strong>read value</strong>的缩写。</li>
<li>左值是指<strong>存储在内存中、有明确存储地址(可取地址)的数据。</strong></li>
<li>右值是指<strong>可以提供数据值的数据(不可取地址)。</strong></li>
</ul>
<p>通过描述可以看出，区分左值与右值的便捷方法是：<strong>可以对表达式取地址(&amp;)就是左值，否则为右值。</strong></p>
<p>所有有名字的变量或者对象都是左值，而右值是匿名的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">520</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">1314</span><span class="p">;</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">b</span><span class="p">;</span>
</code></pre></div><p>一般情况下，位于 = 前面的表达式为左值，位于 = 后边的表达式为右值。也就是说例子中的a,b为左值，520,1314为右值。a=b是一种特殊情况，在这个表达式中a，b都是左值，因为变量b是可以被取地址的，不能视为右值。</p>
<p>C++11中右值可以分为两种：一个是将亡值，另一个则是纯右值：</p>
<ul>
<li><strong>纯右值：非引用返回的临时变量、运算表达式产生的临时变量、原始字面量和lambada表达式等。</strong></li>
<li><strong>将亡值：与右值引用相关的表达式，比如，T&amp;&amp;类型函数的返回值、std::move的返回值等。</strong></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="kt">int</span> <span class="n">value</span> <span class="o">=</span> <span class="mi">520</span><span class="p">;</span>
</code></pre></div><p>上面语句中的value为左值，520为字面量也就是右值。其中value可以被引用，但是520就不行了，因为字面量都是右值。</p>
<h2 id="2右值引用的使用">2、右值引用的使用</h2>
<p>右值引用就是对一个右值进行引用的类型。因为右值是匿名的，所以我们只能通过引用它的方式找到它。<strong>无论声明左值引用还是右值引用都必须立即进行初始化，因为引用类型本身并不拥有所绑定对象的内存，只是该对象的一个别名。通过右值引用的声明，该右值又&quot;重获新生&quot;。</strong></p>
<p>其生命周期与右值引用类型变量的声明周期一样，只要该变量还活着，该右值临时量将会一直存活下去。</p>
<p>关于右值引用的使用，参考代码如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span><span class="cp"></span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span><span class="o">&amp;&amp;</span> <span class="n">value</span> <span class="o">=</span> <span class="mi">520</span><span class="p">;</span>
<span class="k">class</span> <span class="nc">Test</span>
<span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
    <span class="n">Test</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;construct: my name is jerry&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">Test</span><span class="p">(</span><span class="k">const</span> <span class="n">Test</span><span class="o">&amp;</span> <span class="n">a</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;copy construct: my name is tom&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">};</span>

<span class="n">Test</span> <span class="nf">getObj</span><span class="p">()</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="n">Test</span><span class="p">();</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">a1</span><span class="p">;</span>
    <span class="kt">int</span> <span class="o">&amp;&amp;</span><span class="n">a2</span> <span class="o">=</span> <span class="n">a1</span><span class="p">;</span>        <span class="c1">// error
</span><span class="c1"></span>    <span class="n">Test</span><span class="o">&amp;</span> <span class="n">t</span> <span class="o">=</span> <span class="n">getObj</span><span class="p">();</span>   <span class="c1">// error
</span><span class="c1"></span>    <span class="n">Test</span> <span class="o">&amp;&amp;</span> <span class="n">t</span> <span class="o">=</span> <span class="n">getObj</span><span class="p">();</span>
    <span class="k">const</span> <span class="n">Test</span><span class="o">&amp;</span> <span class="n">t</span> <span class="o">=</span> <span class="n">getObj</span><span class="p">();</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><ul>
<li>在上面的例子中<strong>int &amp;&amp;value=520;<strong>里面的520为纯右值，<strong>value</strong>是字面量</strong>520</strong>这个右值的引用。</li>
<li>在<strong>int &amp;&amp;a2=a1</strong>中；<strong>a1</strong>虽然写在**=**的右边，但是它仍然是一个左值，使**用左值初始化一个右值引用是不合法的。**</li>
<li>**Test &amp; t=getObj()**这段代码语法是错误的，<strong>右值不能给普通的左值引用赋值。</strong></li>
<li><strong>Test &amp;&amp; t=getObj()<strong>中getObj()返回的</strong>临时对象称为将亡值，t是这个将亡值的右值引用。</strong></li>
<li>**const Test &amp; t=getObj()**这段代码语法是正确的，<strong>常量左值引用是一个万能的引用类型，它可以接受左值、右值、常量左值和常量右值。</strong></li>
</ul>
<h2 id="3性能优化">3、性能优化</h2>
<p>在C++中在进行对象赋值操作的时候，很多情况下会发生对象之间的深拷贝，如果堆内存很大，这个拷贝的代价也就非常大，在某些情况下，如果想要避免对象的深拷贝，就可以使用右值引用进行性能优化。</p>
<p>修改以下上面的代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span><span class="cp"></span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="k">class</span> <span class="nc">Test</span>
<span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
    <span class="n">Test</span><span class="p">()</span> <span class="o">:</span> <span class="n">m_num</span><span class="p">(</span><span class="k">new</span> <span class="kt">int</span><span class="p">(</span><span class="mi">100</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;construct: my name is jerry&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">Test</span><span class="p">(</span><span class="k">const</span> <span class="n">Test</span><span class="o">&amp;</span> <span class="n">a</span><span class="p">)</span> <span class="o">:</span> <span class="n">m_num</span><span class="p">(</span><span class="k">new</span> <span class="kt">int</span><span class="p">(</span><span class="o">*</span><span class="n">a</span><span class="p">.</span><span class="n">m_num</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;copy construct: my name is tom&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="o">~</span><span class="n">Test</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="k">delete</span> <span class="n">m_num</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">int</span><span class="o">*</span> <span class="n">m_num</span><span class="p">;</span>
<span class="p">};</span>

<span class="n">Test</span> <span class="nf">getObj</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Test</span> <span class="n">t</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">t</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Test</span> <span class="n">t</span> <span class="o">=</span> <span class="n">getObj</span><span class="p">();</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;t.m_num: &#34;</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">t</span><span class="p">.</span><span class="n">m_num</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div><p>测试结果如下：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161702687.png"
        data-srcset="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161702687.png, https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161702687.png 1.5x, https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161702687.png 2x"
        data-sizes="auto"
        alt="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161702687.png"
        title="image-20220816170224552" /></p>
<p>通过输出结果我们可以看到调用Test t=getObj()的时候调用拷贝构造函数对返回的临时对象进行了深拷贝得到对象t，在getObj()函数中创建的对象虽然进行了内存申请操作，但是没有使用就释放掉了。如果能够使用临时对象已经申请的资源，既能节省资源，还能节省申请和释放的时间，如果要执行这样的操作就需要使用右值引用了。</p>
<p><strong>右值引用具有移动语义，移动语义可将资源(堆、系统对象等)通过浅拷贝从一个对象转义到另一个对象这样就能减少不必要的临时对象的创建、拷贝及销毁，可以大幅度提高C++应用程序的性能。</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span><span class="cp"></span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="k">class</span> <span class="nc">Test</span>
<span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
    <span class="n">Test</span><span class="p">()</span> <span class="o">:</span> <span class="n">m_num</span><span class="p">(</span><span class="k">new</span> <span class="kt">int</span><span class="p">(</span><span class="mi">100</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;construct: my name is jerry&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">Test</span><span class="p">(</span><span class="k">const</span> <span class="n">Test</span><span class="o">&amp;</span> <span class="n">a</span><span class="p">)</span> <span class="o">:</span> <span class="n">m_num</span><span class="p">(</span><span class="k">new</span> <span class="kt">int</span><span class="p">(</span><span class="o">*</span><span class="n">a</span><span class="p">.</span><span class="n">m_num</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;copy construct: my name is tom&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// 添加移动构造函数
</span><span class="c1"></span>    <span class="n">Test</span><span class="p">(</span><span class="n">Test</span><span class="o">&amp;&amp;</span> <span class="n">a</span><span class="p">)</span> <span class="o">:</span> <span class="n">m_num</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">m_num</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">a</span><span class="p">.</span><span class="n">m_num</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;move construct: my name is sunny&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="o">~</span><span class="n">Test</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="k">delete</span> <span class="n">m_num</span><span class="p">;</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;destruct Test class ...&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">int</span><span class="o">*</span> <span class="n">m_num</span><span class="p">;</span>
<span class="p">};</span>

<span class="n">Test</span> <span class="nf">getObj</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Test</span> <span class="n">t</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">t</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Test</span> <span class="n">t</span> <span class="o">=</span> <span class="n">getObj</span><span class="p">();</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;t.m_num: &#34;</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">t</span><span class="p">.</span><span class="n">m_num</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div><p>测试代码执行结果如下：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161712211.png"
        data-srcset="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161712211.png, https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161712211.png 1.5x, https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161712211.png 2x"
        data-sizes="auto"
        alt="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208161712211.png"
        title="image-20220816171225155" /></p>
<p>通过修改，在上面的代码给Test类添加了**移动构造函数(参数为右值引用类型)，**这样在进行Test t=getObj()操作时候并没有调用拷贝构造函数进行深拷贝，而是调用移动构造函数，这个函数中只是进行了浅拷贝，没有对临时对象进行深拷贝，提高性能。</p>
<p>如果不使用移动构造，在执行Test t=getObj()时候也是进行浅拷贝，但是临时对象被析构的时候，类成员指针int *m_num指向的内存也就被析构了，对象t也就无法访问这块内存地址了。</p>
<p>在测试程序中getObj()的返回值就是一个将亡值，也就是一个右值，在进行赋值操作的时候如果右边是一个右值，那么移动构造就会被调用。<strong>移动构造中使用了右值引用，会将临时对象的堆内存地址的所有权转移给对象t，这块内存被成功续命，因此在t对象中还可以继续使用这块内存。</strong></p>
<p><strong>注意：</strong></p>
<p><strong>对于需要动态申请大量资源的类，应该设计移动构造函数，以提高程序效率。需要注意的是，我们一般在提供移动构造函数的同时，也会提供常量左值引用的拷贝构造函数，以保证移动不成还可以使用拷贝构造函数。</strong></p>
<h2 id="4的特性">4、&amp;&amp;的特性</h2>
<p>在 C++ 中，并不是所有情况下 **&amp;&amp; 都代表是一个右值引用，**具体的场景体现在模板和自动类型推导中，如果是模板参数需要指定为 T&amp;&amp;，如果是自动类型推导需要指定为 auto &amp;&amp;，在这两种场景下 &amp;&amp; 被称作未定的引用类型。另外还有一点需要额外注意 const T&amp;&amp; 表示一个右值引用，不是未定引用类型。</p>
<p>先看第一个例子，在函数模板中使用&amp;&amp;：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kt">void</span> <span class="n">f</span><span class="p">(</span><span class="n">T</span><span class="o">&amp;&amp;</span> <span class="n">param</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">f1</span><span class="p">(</span><span class="k">const</span> <span class="n">T</span><span class="o">&amp;&amp;</span> <span class="n">param</span><span class="p">);</span>
<span class="n">f</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span> 	
<span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">);</span> 
<span class="n">f1</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
</code></pre></div><p>在上面的例子中函数模板进行了自动类型推导，需要通过传入的实参来确定参数param的实际类型。</p>
<ul>
<li>第4行中，对于f(10)来说传入的实参10是右值，因此T&amp;&amp;表示右值引用。</li>
<li>第6行中，对于f(x)来说传入的实参x是左值，因此T&amp;&amp;表示左值引用。</li>
<li>第7行中，f1(x)的参数是const T&amp;&amp;不是未定引用类型，不需要推导，本身就表示一个右值引用。</li>
</ul>
<p>再来看第二个例子：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">520</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">1314</span><span class="p">;</span>
    <span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">v1</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
    <span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">v2</span> <span class="o">=</span> <span class="mi">250</span><span class="p">;</span>
    <span class="k">decltype</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="o">&amp;&amp;</span> <span class="n">v3</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span>   <span class="c1">// error
</span><span class="c1"></span>    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;v1: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">v1</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;, v2: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">v2</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div><ul>
<li>第4行中**auto&amp;&amp;**表示一个整形的左值引用。</li>
<li>第5行中**auto&amp;&amp;**表示一个整形的右值引用。</li>
<li>第6行中**decltype(x)&amp;&amp;<strong>等价于</strong>int&amp;&amp;**是一个右值引用不是未定义引用类型，y是一个左值，<strong>不能使用左值初始化一个右值引用类型。</strong></li>
</ul>
<p>由于上述代码存在T&amp;&amp;或者auto&amp;&amp;这种未定义引用类型，当它作为参数时，有可能被一个右值引用初始化，也有可能被一个左值引用初始化，在进行类型推导时右值引用类型(&amp;&amp;)会发生变化，这种变化被称为引用折叠。在C++11中引用折叠规则如下：</p>
<ul>
<li>
<p><strong>通过右值推导T&amp;&amp;或者auto&amp;&amp;得到的是一个右值引用类型。</strong></p>
</li>
<li>
<p><strong>通过非右值(右值引用、左值、左值引用、常量右值引用、常量左值引用)推导T&amp;&amp;或者auto&amp;&amp;得到的是一个左值引用类型。</strong></p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="kt">int</span><span class="o">&amp;&amp;</span> <span class="n">a1</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
<span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">bb</span> <span class="o">=</span> <span class="n">a1</span><span class="p">;</span>
<span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">bb1</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>

<span class="kt">int</span> <span class="n">a2</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
<span class="kt">int</span> <span class="o">&amp;</span><span class="n">a3</span> <span class="o">=</span> <span class="n">a2</span><span class="p">;</span>
<span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">cc</span> <span class="o">=</span> <span class="n">a3</span><span class="p">;</span>
<span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">cc1</span> <span class="o">=</span> <span class="n">a2</span><span class="p">;</span>

<span class="k">const</span> <span class="kt">int</span><span class="o">&amp;</span> <span class="n">s1</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span><span class="o">&amp;&amp;</span> <span class="n">s2</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">dd</span> <span class="o">=</span> <span class="n">s1</span><span class="p">;</span>
<span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">ee</span> <span class="o">=</span> <span class="n">s2</span><span class="p">;</span>

<span class="k">const</span> <span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
</code></pre></div><ul>
<li>第2行：a1为右值引用，推导出bb为左值引用类型。</li>
<li>第3行：5为右值，推导出的bb1为右值引用类型。</li>
<li>第7行：a3为左值引用，推导出的cc为左值引用。</li>
<li>第8行：a2为左值引用，推导出的cc1为左值引用。</li>
<li>第12行：s1为常量左值引用，推导出的dd为常量左值引用。</li>
<li>第13行：s2为常量右值引用，推导出的ee为常量右值引用。</li>
<li>第15行：x为右值引用，不需要推导，只能通过右值初始化。</li>
</ul>
<p>最后再看一个例子：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span><span class="cp"></span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">void</span> <span class="nf">printValue</span><span class="p">(</span><span class="kt">int</span> <span class="o">&amp;</span><span class="n">i</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;l-value: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">printValue</span><span class="p">(</span><span class="kt">int</span> <span class="o">&amp;&amp;</span><span class="n">i</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;r-value: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">forward</span><span class="p">(</span><span class="kt">int</span> <span class="o">&amp;&amp;</span><span class="n">k</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">printValue</span><span class="p">(</span><span class="n">k</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">520</span><span class="p">;</span>
    <span class="n">printValue</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
    <span class="n">printValue</span><span class="p">(</span><span class="mi">1314</span><span class="p">);</span>
    <span class="n">forward</span><span class="p">(</span><span class="mi">250</span><span class="p">);</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div><p>测试代码结果如下：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171011704.png"
        data-srcset="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171011704.png, https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171011704.png 1.5x, https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171011704.png 2x"
        data-sizes="auto"
        alt="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171011704.png"
        title="image-20220817101112650" /></p>
<p>根据测试代码可以得知，编译器会根据传入的参数的类型（左值还是右值）调用对应的重置函数（printValue），函数 forward () 接收的是一个右值，但是在这个函数中调用函数 printValue () 时，参数 k 变成了一个命名对象，编译器会将其当做左值来处理。</p>
<p>最后总结以下关于&amp;&amp;的使用：</p>
<ul>
<li><strong>左值和右值是独立于他们的类型的，右值引用类型可能是左值也可能是右值。</strong></li>
<li><strong>编译器会将已命名的右值视为左值，将未命名的右值视为右值。</strong></li>
<li><strong>auto&amp;&amp;或者参数类型自动类型推导的T&amp;&amp;是一个未定的引用类型，它可能是左值引用也可能是右值引用类型，这取决于初始化的值类型。</strong></li>
<li><strong>通过右值推导T&amp;&amp;或者auto&amp;&amp;得到的是一个右值引用类型，其余都是左值引用类型。</strong></li>
</ul>
<h2 id="5stdmove">5、std::move</h2>
<p>在C++11中添加了右值引用，并且不能使用左值初始化右值引用，如果想要使用左值初始化一个右值引用需要借助std::move()函数，<strong>使用std::move方法可以将左值转化为右值。使用这个函数并不能移动任何东西，而是和移动构造函数一样具有移动语义，将对象的状态或者所有权从一个对象移动到另一个对象上，只是转移，没有内存拷贝。</strong></p>
<p>从实现上讲，std::move基本等同于一个类型转换：**static_cast&lt;T&amp;&amp;&gt;(lvalue)；**函数原型如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="k">template</span><span class="o">&lt;</span><span class="k">class</span> <span class="nc">_Ty</span><span class="o">&gt;</span>
<span class="n">_NODISCARD</span> <span class="k">constexpr</span> <span class="n">remove_reference_t</span><span class="o">&lt;</span><span class="n">_Ty</span><span class="o">&gt;&amp;&amp;</span> <span class="n">move</span><span class="p">(</span><span class="n">_Ty</span><span class="o">&amp;&amp;</span> <span class="n">_Arg</span><span class="p">)</span> <span class="n">_NOEXCEPT</span>
<span class="p">{</span>	<span class="c1">// forward _Arg as movable
</span><span class="c1"></span>    <span class="k">return</span> <span class="p">(</span><span class="k">static_cast</span><span class="o">&lt;</span><span class="n">remove_reference_t</span><span class="o">&lt;</span><span class="n">_Ty</span><span class="o">&gt;&amp;&amp;&gt;</span><span class="p">(</span><span class="n">_Arg</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div><p>使用方法如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="k">class</span> <span class="nc">Test</span>
<span class="p">{</span>
<span class="k">public</span><span class="err">：</span>
    <span class="n">Test</span><span class="p">(){}</span>
    <span class="p">......</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Test</span> <span class="n">t</span><span class="p">;</span>
    <span class="n">Test</span> <span class="o">&amp;&amp;</span> <span class="n">v1</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span>          <span class="c1">// error
</span><span class="c1"></span>    <span class="n">Test</span> <span class="o">&amp;&amp;</span> <span class="n">v2</span> <span class="o">=</span> <span class="n">move</span><span class="p">(</span><span class="n">t</span><span class="p">);</span>    <span class="c1">// ok
</span><span class="c1"></span>    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><ul>
<li>第10行中，使用左值初始化右值引用，因此语法错误。</li>
<li>第11行中，使用move函数将左值转化为右值，这样就可以初始化右值引用了。</li>
</ul>
<p>假设一个临时容器很大，并且需要将这个容器赋值给另一个容器，就可以执行如下操作：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="n">list</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ls</span><span class="p">;</span>
<span class="n">ls</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">);</span>
<span class="n">ls</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="s">&#34;world&#34;</span><span class="p">);</span>
<span class="p">......</span>
<span class="n">list</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ls1</span> <span class="o">=</span> <span class="n">ls</span><span class="p">;</span>        <span class="c1">// 需要拷贝, 效率低
</span><span class="c1"></span><span class="n">list</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ls2</span> <span class="o">=</span> <span class="n">move</span><span class="p">(</span><span class="n">ls</span><span class="p">);</span>
</code></pre></div><p>如果不使用std::move，拷贝的代价很大，性能较低。使用move几乎没有任何代价，只是转换了资源的所有权，如果一个对象内部有较大的堆内存或者动态数组时，使用move就可以非常方便的进行数据所有权的转移。另外，我们也可以给类编写相应的移动构造函数**(T::T(T&amp;&amp;another))**和具有移动语义的赋值函数**(T&amp;&amp;T::operator=(T&amp;&amp;rhs))**，在构造对象和赋值的时候尽可能的进行资源的重复利用，因为它们都是接收一个右值引用参数。</p>
<h2 id="6stdforward">6、std::forward</h2>
<p>右值引用类型是独立于值的，一个右值引用作为函数的形参时，在函数内部转发该参数给内部其他函数时，它就变成了一个左值，并不是原来的类型了。如果需要按照原来的类型转发到另一个函数，<strong>可以使用C++11提供的std::forward函数，该函数实现的功能称之为完美转发。</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="c1">// 函数原型
</span><span class="c1"></span><span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">T</span><span class="o">&gt;</span> <span class="n">T</span><span class="o">&amp;&amp;</span> <span class="n">forward</span> <span class="p">(</span><span class="k">typename</span> <span class="n">remove_reference</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">type</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">)</span> <span class="k">noexcept</span><span class="p">;</span>
<span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">T</span><span class="o">&gt;</span> <span class="n">T</span><span class="o">&amp;&amp;</span> <span class="n">forward</span> <span class="p">(</span><span class="k">typename</span> <span class="n">remove_reference</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">type</span><span class="o">&amp;&amp;</span> <span class="n">t</span><span class="p">)</span> <span class="k">noexcept</span><span class="p">;</span>

<span class="c1">// 精简之后的样子
</span><span class="c1"></span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">);</span>
</code></pre></div><ul>
<li>当T为左值引用类型时，t将被转化为T类型的左值。</li>
<li>当T不是左值引用类型时，t将被转化为T类型的右值。</li>
</ul>
<p>下面通过一个例子演示一下关于forward的使用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-CPP" data-lang="CPP"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span><span class="cp"></span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kt">void</span> <span class="n">printValue</span><span class="p">(</span><span class="n">T</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;l-value: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">t</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kt">void</span> <span class="n">printValue</span><span class="p">(</span><span class="n">T</span><span class="o">&amp;&amp;</span> <span class="n">t</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;r-value: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">t</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kt">void</span> <span class="n">testForward</span><span class="p">(</span><span class="n">T</span> <span class="o">&amp;&amp;</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">printValue</span><span class="p">(</span><span class="n">v</span><span class="p">);</span>
    <span class="n">printValue</span><span class="p">(</span><span class="n">move</span><span class="p">(</span><span class="n">v</span><span class="p">));</span>
    <span class="n">printValue</span><span class="p">(</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">v</span><span class="p">));</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">testForward</span><span class="p">(</span><span class="mi">520</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">num</span> <span class="o">=</span> <span class="mi">1314</span><span class="p">;</span>
    <span class="n">testForward</span><span class="p">(</span><span class="n">num</span><span class="p">);</span>
    <span class="n">testForward</span><span class="p">(</span><span class="n">forward</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">num</span><span class="p">));</span>
    <span class="n">testForward</span><span class="p">(</span><span class="n">forward</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&amp;&gt;</span><span class="p">(</span><span class="n">num</span><span class="p">));</span>
    <span class="n">testForward</span><span class="p">(</span><span class="n">forward</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&amp;&amp;&gt;</span><span class="p">(</span><span class="n">num</span><span class="p">));</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>测试代码的结果如下：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171101969.png"
        data-srcset="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171101969.png, https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171101969.png 1.5x, https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171101969.png 2x"
        data-sizes="auto"
        alt="https://raw.githubusercontent.com/jjking20010901/typora/master/img202208171101969.png"
        title="image-20220817110147910" /></p>
</div><div class="post-footer" id="post-footer">
    <div class="post-info"><div class="post-info-tag"><span><a href="/tags/%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8%E5%92%8C%E5%AE%8C%E7%BE%8E%E8%BD%AC%E5%8F%91/">右值引用和完美转发</a>
                </span></div><div class="post-info-line"><div class="post-info-mod">
                <span>更新于 2022-08-16</span>
            </div><div class="post-info-mod"></div>
        </div><div class="post-info-share">
            <span><a href="javascript:void(0);" title="分享到 Twitter" data-sharer="twitter" data-url="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" data-title="右值引用和完美转发" data-hashtags="右值引用和完美转发"><i class="fab fa-twitter fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Facebook" data-sharer="facebook" data-url="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" data-hashtag="右值引用和完美转发"><i class="fab fa-facebook-square fa-fw"></i></a><a href="javascript:void(0);" title="分享到 WhatsApp" data-sharer="whatsapp" data-url="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" data-title="右值引用和完美转发" data-web><i class="fab fa-whatsapp fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Line" data-sharer="line" data-url="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" data-title="右值引用和完美转发"><i class="fab fa-line fa-fw"></i></a><a href="javascript:void(0);" title="分享到 微博" data-sharer="weibo" data-url="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" data-title="右值引用和完美转发" data-image="https://img-blog.csdnimg.cn/img_convert/c592952aec4e1c875073f2a3d970cde5.png#pic_center"><i class="fab fa-weibo fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Myspace" data-sharer="myspace" data-url="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" data-title="右值引用和完美转发" data-description="右值引用和完美转发"><i data-svg-src="/lib/simple-icons/icons/myspace.min.svg"></i></a><a href="javascript:void(0);" title="分享到 Blogger" data-sharer="blogger" data-url="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" data-title="右值引用和完美转发" data-description="右值引用和完美转发"><i class="fab fa-blogger fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Evernote" data-sharer="evernote" data-url="https://acking-you.gitee.io/posts/%C3%A5%C3%A5%C3%A5%C3%A7%C3%A5%C3%A5%C3%A7%C3%A8%C3%A5/" data-title="右值引用和完美转发"><i class="fab fa-evernote fa-fw"></i></a></span>
        </div></div><div class="post-nav"><a href="/posts/%C3%A9%C3%A5%C2%BA%C3%A6%C3%A5%C3%A7%C3%A5%C3%A6%C3%A7/" class="prev" rel="prev" title="长度最小的子数组"><i class="fas fa-angle-left fa-fw"></i>Previous Post</a>
            <a href="/posts/%C3%A8%C2%BA%C3%A6%C3%A7%C3%A9%C2%B5%C3%A2-/" class="next" rel="next" title="螺旋矩阵Ⅱ">Next Post<i class="fas fa-angle-right fa-fw"></i></a></div></div>
</div></article></div>
            </main>
            <footer class="footer"><div class="footer-container"><div class="footer-line">由 <a href="https://gohugo.io/" target="_blank" rel="noopener noreffer" title="Hugo 0.86.0">Hugo</a> 强力驱动 | 主题 - <a href="https://github.com/khusika/FeelIt" target="_blank" rel="noopener noreffer" title="FeelIt 1.0.1"><i class="fas fa-hand-holding-heart fa-fw"></i> FeelIt</a>
        </div><div class="footer-line" itemscope itemtype="http://schema.org/CreativeWork"><i class="far fa-copyright fa-fw"></i><span itemprop="copyrightYear">2022</span><span class="author" itemprop="copyrightHolder">&nbsp;<a href="/"></a></span></div>
</div>
</footer>
        </div>

        <div id="fixed-buttons"><a href="#" id="back-to-top" class="fixed-button" title="回到顶部">
                <i class="fas fa-chevron-up fa-fw"></i>
            </a></div><link rel="stylesheet" href="/lib/fontawesome-free/all.min.css"><link rel="stylesheet" href="/lib/animate/animate.min.css"><link rel="stylesheet" href="/lib/katex/katex.min.css"><link rel="stylesheet" href="/lib/katex/copy-tex.min.css"><script src="/lib/autocomplete/autocomplete.min.js"></script><script src="/lib/lunr/lunr.min.js"></script><script src="/lib/lunr/lunr.stemmer.support.min.js"></script><script src="/lib/lunr/lunr.zh.min.js"></script><script src="/lib/lazysizes/lazysizes.min.js"></script><script src="/lib/clipboard/clipboard.min.js"></script><script src="/lib/sharer/sharer.min.js"></script><script src="/lib/katex/katex.min.js"></script><script src="/lib/katex/auto-render.min.js"></script><script src="/lib/katex/copy-tex.min.js"></script><script src="/lib/katex/mhchem.min.js"></script><script>window.config={"code":{"copyTitle":"复制到剪贴板","maxShownLines":200},"comment":{},"math":{"delimiters":[{"display":true,"left":"$$","right":"$$"},{"display":true,"left":"\\[","right":"\\]"},{"display":false,"left":"$","right":"$"},{"display":false,"left":"\\(","right":"\\)"}],"strict":false},"search":{"highlightTag":"em","lunrIndexURL":"/index.json","lunrLanguageCode":"zh","lunrSegmentitURL":"/lib/lunr/lunr.segmentit.js","maxResultLength":100,"noResultsFound":"没有找到结果","snippetLength":50,"type":"lunr"}};</script><script src="/js/theme.min.js"></script></body></html>
